﻿#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>


//int main()
//{
//	int a = 10;
//	printf("%zd\n", sizeof(a));//4
//	printf("%zd\n", sizeof a);//4
//	printf("%zd\n", sizeof(int));//4
//
//	return 0;
//}

//int main()
//{
//	char arr1[3] = { 'a', 'b', 'c' };// a b c .....
//	char arr2[] = "abc";			 // a b c \0
//	printf("%zd\n", strlen(arr1));	 // 随机值
//	printf("%zd\n", strlen(arr2));	 //3
//
//	printf("%zd\n", sizeof(arr1));	//3
//	printf("%zd\n", sizeof(arr2));	//4
//	return 0;
//}

//int main()
//{
//	int a[] = { 1,2,3,4 };
//	printf("%zd\n", sizeof(a));			// sizeof(a) → sizeof(数组名)  属于特殊范畴， 这里的一维数组名不再是首元素地址，而是表示整个一维数组，一共四个元素，每个都是int型，4*4=16
//	printf("%zd\n", sizeof(a + 0));		// a+0 不是 数组名 ，所以这里的a是一维数组名，代表一维数组首元素的地址，a+0还是一维数组首元素的地址，4/8
//	printf("%zd\n", sizeof(*a));		// 对一维数组首元素地址解引用得到的首个元素，每个元素都是int型，4
//	printf("%zd\n", sizeof(a + 1));		// 与2类似，这里指代一维数组中第二个元素的地址，4/8
//	printf("%zd\n", sizeof(a[1]));		// a[1] 相当于 *(a+1),对一维数组中的第二个元素解引用，得到一维数组中第二个元素，为int型，4
//										// 一维数组中的a[1]，本身就是第二个元素
//	printf("%zd\n", sizeof(&a));		// &a,属于特殊范畴，得到的是整个一维数组的地址，4/8
//	printf("%zd\n", sizeof(*&a));		// *& 相互抵消，括号内仍是 一维数组名a，sizeof(数组名)，属于特殊范畴，16
//	printf("%zd\n", sizeof(&a + 1));	// 取地址a，属于特殊范畴，得到的是整个一维数组的地址，指针类型为 int (*)[4]  +1是加一整个一维数组，所以+1后，指针会指向 4 后面 ，
//										// 仍是地址，4/8
//	printf("%zd\n", sizeof(&a[0]));		// 对首个元素取地址，4/8
//	printf("%zd\n", sizeof(&a[0] + 1));	// 第二个元素的地址 4/8
//}

//
//int main()
//{
//	char arr[] = { 'a','b','c','d','e','f' };
//	printf("%zd\n", sizeof(arr));		//6
//	printf("%zd\n", sizeof(arr + 0));	//一维数组数组名（代表首元素地址） + 0 仍是一维数组首元素地址 ，4/8
//	printf("%zd\n", sizeof(*arr));		//对一维数组数组名（首元素地址）解引用，得到首个元素，为char型，1
//	printf("%zd\n", sizeof(arr[1]));		//arr[1] 相当于 *(arr+1),arr是首元素地址，+1为第二个元素地址，*(arr +1 ) 解引用得到第二个元素，也为char型号，1
//	printf("%zd\n", sizeof(&arr));		//取地址数组名，属于特殊范畴，取出整个数组的地址，4/8
//	printf("%zd\n", sizeof(&arr + 1));	//同上，+1后 指针会指向下一个数组的地址 4/8
//	printf("%zd\n", sizeof(&arr[0] + 1));//第二个元素的地址 4/8
//}


//
//int main()
//{
//	char arr[] = { 'a','b','c','d','e','f' };
//	printf("%zd\n", strlen(arr));		//strlen 只关系地址从哪开始，数组名是首元素的地址，那strlen就从这里开始计数，直到遇到\0为止，随机数
//	printf("%zd\n", strlen(arr + 0));	//首元素地址+0仍是首元素地址，随机数
//	//printf("%d\n", strlen(*arr));		//解引用首元素地址，得到首元素，将首元素的ascii码值作为地址去计数，非法代码，error
//	//printf("%d\n", strlen(arr[1]));	//同上
//	printf("%zd\n", strlen(&arr));		//取地址数组名，特殊范畴，得到的是整个数组的地址，因为整个数组的起始地址，和首元素的地址是一样的，所以strlen仍会从首个地址直至\0，随机数
//	printf("%zd\n", strlen(&arr + 1));	//取地址数组名，特殊范畴，取出的是整个数组的地址，指针类型为 char (*)[6],因此+1会跳过整个数组，从而指针会指向下一个数组，随机数，但是比上一个随机数小6
//	printf("%zd\n", strlen(&arr[0] + 1));//对数组中第一个元素取地址，指针类型为 char * ，+1 得到第二个元素的地址，strlen 会从第二个元素的地址往后计数，直至\0，比上上个随机数小1
//}

//
//int main()
//{
//	char arr[] = "abcdef";
//	printf("%zd\n", sizeof(arr));			//sizeof(数组名)，特殊范畴，数组名不再是首元素地址，而是指代整个数组，7
//	printf("%zd\n", sizeof(arr + 0));		//首元素地址 + 0 仍是首元素地址，4/8
//	printf("%zd\n", sizeof(*arr));			//解引用首元素地址，字符串类型为char，1
//	printf("%zd\n", sizeof(arr[1]));		//arr[1] = *(arr+1) 一维数组的第二个元素，1
//	printf("%zd\n", sizeof(&arr));			//取地址数组名，特殊范畴，指代整个数组的地址，但仍属于地址，4/8
//	printf("%zd\n", sizeof(&arr + 1));		//取地址数组名，特殊范畴，指代整个数组的地址，指针类型为char (*)[6],+1 会跳过整个一维数组，指针会指向下一个数组，但仍属于地址，4/8
//	printf("%zd\n", sizeof(&arr[0] + 1));	//对一维数组第一个元素取地址，指针类型为 char*， +1后为一维数组中第二个元素的地址，4/8
//}

//int main()
//{
//	char arr[] = "abcdef";
//	printf("%zd\n", strlen(arr));			//一维数组首元素地址，6
//	printf("%zd\n", strlen(arr + 0));		//+0仍是一维数组首元素的地址，6
//	//printf("%zd\n", strlen(*arr));		//error
//	//printf("%zd\n", strlen(arr[1]));		//error
//	printf("%zd\n", strlen(&arr));			//取地址数组名，取出整个数组的地址，因为整个数组的首个地址，和一维数组首元素的地址是一样的，所以strlen还是从数组首个地址开始计算，直至\0，6
//	printf("%zd\n", strlen(&arr + 1));		//取地址数组名，取出整个数组的地址，因为指针类型为 char (*) [6] ,+1会跳过整个数组，指针会指向下一个数组，随机值
//	printf("%zd\n", strlen(&arr[0] + 1));	//取地址首个元素，指针类型 char*，+1是第二个元素地址，5
//}


//int main()
//{
//	char* p = "abcdef";
//	printf("%zd\n", sizeof(p));			//p是首元素的地址，4/8
//	printf("%zd\n", sizeof(p + 1));		//p的类型是 char*， +1后是第二个元素的地址，4/8
//	printf("%zd\n", sizeof(*p));		//1
//	printf("%zd\n", sizeof(p[0]));		//p[0] = * (p+0)  ,1
//	printf("%zd\n", sizeof(&p));		//&p,对 p取地址，二级指针，4/8
//	printf("%zd\n", sizeof(&p + 1));	//4/8
//	printf("%zd\n", sizeof(&p[0] + 1));	//首个元素地址取地址，+1后为第二个元素的地址，4/8
//}

//int main()
//{
//	char* p = "abcdef";					//末尾存在\0
//	printf("%zd\n", strlen(p));			//p是a的地址，6
//	printf("%zd\n", strlen(p + 1));		//p+1是b的地址，5
//	//printf("%zd\n", strlen(*p));		//只元素a，error
//	//printf("%zd\n", strlen(p[0]));		//指元素a，error
//	printf("%zd\n", strlen(&p));		//对指针p取地址，二级指针，随机数
//	printf("%zd\n", strlen(&p + 1));	//二级指针的地址加1，指针类型为 char** ，随机数
//	printf("%zd\n", strlen(&p[0] + 1)); //对首元素取地址+1，得到是b的地址，5
//}


//int main()
//{
//	int a[3][4] = { 0 };					//3*4的二维数组，可以视为由3个元素个数为4的一维数组组成
//	printf("%zd\n", sizeof(a));				//sizeof(数组名)，特殊范畴，指代整个二维数组，3*4*4(int 类型 字节大小) = 48
//	printf("%zd\n", sizeof(a[0][0]));		//二维数组首个元素，因为二维数组是int型，所以是4
//	printf("%zd\n", sizeof(a[0]));			//二维数组中，一维数组的数组名为a[]，所以sizeof(数组名)，属于特殊范畴，指代整个一维数组的，所以是4*4=16
//	printf("%zd\n", sizeof(a[0] + 1));		//二维数组中，一维数组的数组名，一维数组的数组名是首个元素的地址，指针类型为 int*，+1指向该行一维数组的第二个元素的地址，4/8
//	printf("%zd\n", sizeof(*(a[0] + 1)));	//第一行，第二个元素，4
//	printf("%zd\n", sizeof(a + 1));			//二维数组数组名，指针类型为 int (*)[4]，+1 会跳过第一个一维数组的地址，从而指向第二个一维数组的首地址，4/8
//	printf("%zd\n", sizeof(*(a + 1)));		//对二维数组中，第二个一维数组解引用，得到整个一维数组，16
//	printf("%zd\n", sizeof(&a[0] + 1));		//取地址数组名，特殊范畴，指代二维数组中，整个一维数组的地址，+1 指向二维数组中第二行一维数组的整个地址，4/8
//	printf("%zd\n", sizeof(*(&a[0] + 1)));	//16
//	printf("%zd\n", sizeof(*a));				//二维数组数组名，指代二维数组首行数组地址，*a得到二维数组首个元素，16
//	printf("%zd\n", sizeof(a[3]));			//a[3] = *(a+3), sizeof(表达式) 括号内的表达式不会计算，因此vs不会报错，16
//}


//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结构是啥？

//struct Test
//{
//	int Num;
//	char* pcName;
//	short sDate;
//	char cha[2];
//	short sBa[4];
//}*p = (struct Test*)0x100000;
//int main()
//{
//	printf("%p\n", p + 0x1);				//p是是结构体指针，结构体指针尚未学习，但题中假设结构体指针的大小为20个字节，因此 p + 0x1，相当于p+20，将20转为16进制 为14，所以答案为0x00100014
//	printf("%p\n", (unsigned long)p + 0x1);	//将结构体指针强行转变为 无符号的long int 型，这里就相当于是整型的+-，因此答案为 0x00100001
//	printf("%p\n", (unsigned int*)p + 0x1); //将结构体指针强行转变为 无符号的int* 型指针，指针指向的数据类型为int型，所以+1 作用到地址上就是+4 因此答案为0x00100004
//	return 0;
//}


//int main()
//{
//	int a[5] = { 1, 2, 3, 4, 5 };
//	int* ptr = (int*)(&a + 1);//取地址数组名，特殊范畴，指代整个数组的地址，+1后，数组指向5 后面的地址，因此ptr就指向该位置， &a+1 本生就是 int*类型的
//	printf("%d,%d", *(a + 1), *(ptr - 1));//前者，一维数组名就是首元素地址，+1为第二个元素的地址，解引用为2
//	return 0;							  //后者，因为ptr指向的是5后面的地址，而ptr本身的指针类型为int* 型，指向的数据类型为int，-1只会向左移动四个字节，所以为5
//}

//int main()
//{
//	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//陷阱逗号表达式，最终结果取最后一个表达式，注意前面的表达式是否对最后的表达式产生影响
//	int* p;									 // 所以实际得到的二维数组为{1,3,5,0,0,0};
//	p = a[0];							     //a[0] 二维数组中，首行一维数组的数组名，即首行一维数组首元素的地址
//	printf("%d", p[0]);						 //p[0] = *(p+0) = *p 对首元素的地址解引用，得到首个元素
//	return 0;
//}



//假设环境是x86环境，程序输出的结果是啥？
//int main()
//{
//	int a[5][5];													 //a是一个5*5的二维数组
//	int(*p)[4] = a;														 //p的指针类型为 int(*)[4]，其中每行元素个数为4,强行让p等于a的地址，会覆盖不全
////	p = a;															 //
//	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);	 // &p[4][2] - &a[4][2] = -4      数组在内存中是由低到高排的，越往后的数字，地址越大 
//																	 //                               他的原码是 10000000000000000000000000000100
//	return 0;														 //								  反码是 11111111111111111111111111111011
//}																	 //								  补码是 11111111111111111111111111111100 转为16进制即 FF FF FF FC
//																	 //%p 输出的是地址，地址不可能为负，所以以补码的形式输出
//																	 //%d 输出的有符号的整型，所以可以是-4
//


//int main()
//{
//	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int* ptr1 = (&aa + 1);
//	int* ptr2 = (*(aa + 1));
//	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//	return 0;
//}

//#include <stdio.h>
//int main()
//{
//	char* a[] = { "work","at","alibaba" };//此处的赋值方式是，将数组内的三个字符串 首个字符的地址分别分别存到 三块内存地址中，，指针类型为 char* 通过解引用该地址达到访问整个字符串的目的
//	char** pa = a;	//a是数组名，数组名是首元素的地址，把首元素的地址传给 pa 因为a本身就已经是指针，所以这里需要定义一个二级指针pa，来接收一级指针的地址
//	pa++;//			//pa++ 改变的是pa中的内容，而*pa改变的pa指向所指向地址中的内容，所以这里pa++没有任何问题，改变的是pa中存放的数组a当中的地址，即原来指向数组首元素地址，后来是第二个元素的地址，解引用*pa得到的是数组a中第二个元素的内容
//	printf("%s\n", *pa);	//因为第二个元素中的内容仍然是地址，%s打印会根据该地址往后打印直到遇到\0停止。
//	return 0;
//}

//int main()
//{
//	int a[2] = { 10 ,20};
//	int* p = a;
//	p++;
//	
//	printf("%p\n", p);
//	printf("%d\n", *p);
//	
//}


int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };			//cp[]数组中，存放的是c[]数组中每个元素的地址，注：这些元素是字符串首字母的地址
	char*** cpp = cp;								//cp是数组名，数组名是首元素地址，所以cpp中存放的cp[]数组中，首元素的地址
	printf("%s\n", **++cpp);			//point
	printf("%s\n", *-- * ++cpp + 3);	//*-- * ++cpp 前面这一步最终会将指针解引用到字符"New"的首元素地址,而对这个地址进行+3的话，最终会从第四个元素开始打印	注：此后开始 cpp的值不发生变化
	printf("%s\n", *cpp[-2] + 3);		//cpp[-2] = *(cpp-2)  **(cpp-2) +3    ST
	printf("%s\n", cpp[-1][-1] + 1);	//cpp[-1][-1] = *(*(cpp-1)-1) +1   EW
	return 0;
}